using System.Net.Sockets;
using System.Net.WebSockets;
using Geek.Server.Core.Net;
using Geek.Server.Core.Net.Websocket;
using Geek.Server.Core.Serialize;
using MongoDB.Bson;
using Newtonsoft.Json;
using NLog;

namespace Geek.Server.DemoClient.Core
{
    public class Client
    {
        private static readonly Logger Log = LogManager.GetCurrentClassLogger();
        private NetChannel netChannel;
        private readonly MsgWaiter msgWaiter = new();
        private uint msgUniId = 0;

        public async Task<bool> ConnectAsync()
        {
            if (TestSettings.Ins.useWebSocket)
            {
                var ws = new ClientWebSocket();
                await ws.ConnectAsync(new Uri(TestSettings.Ins.webSocketServerUrl), CancellationToken.None);

                if (ws.State == WebSocketState.Open)
                {
                    Log.Info($"Connected to {TestSettings.Ins.webSocketServerUrl}");
                    netChannel = new WebSocketChannel(ws, TestSettings.Ins.webSocketServerUrl, OnReceive);
                    _ = netChannel.StartAsync();

                    return true;
                }
                else
                {
                    Log.Error($"连接服务器失败...");
                    return false;
                }
            }
            else
            {
                var socket = new TcpClient(AddressFamily.InterNetwork);
                try
                {
                    socket.NoDelay = true;
                    await socket.ConnectAsync(TestSettings.Ins.serverIp, TestSettings.Ins.serverPort);
                }
                catch (Exception e)
                {
                    Log.Error(e);
                    return false;
                }

                netChannel = new ClientTcpChannel(socket, OnReceive);
                _ = netChannel.StartAsync();

                return true;
            }
        }

        public async Task<T> SendRequestAsync<T>(RequestMessage msg) where T: ResponseMessage, new()
        {
            msg.RpcId = ++msgUniId;
            Log.Info($"发送消息:{JsonConvert.SerializeObject(msg)}");
            var awaiter = msgWaiter.StartWait(msg.RpcId, msg.GetType().Name);
            netChannel.Write(msg);
            
            var resp = await awaiter;
            if (resp is T dstResp)
                return dstResp;

            if (resp.ErrorCode != 0)
            {
                return new T
                {
                    ErrorCode = resp.ErrorCode,
                    RpcId = resp.RpcId
                };
            }

            return new T
            {
                ErrorCode = (int)MessageErrorCode.ResponseTypeNotMatch,
                RpcId = resp.RpcId
            };
        }

        public void OnReceive(Message msg)
        {
            Log.Info($"收到消息:{msg.ToJsonStr()} {MsgFactory.FindType(msg.Route)}");

            if (msg is ResponseMessage responseMessage)
            {
                msgWaiter.EndWait(responseMessage);
            }
        }
    }
}
